package com.snow.auth.component;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.snow.annotation.RequirePermission;
import com.snow.exception.CommonRuntimeException;
import com.snow.exception.LoginUserNotFoundException;
import com.snow.model.ILoginUser;
import com.snow.auth.config.AuthConfig;
import com.snow.auth.entity.SystemTenant;
import com.snow.auth.repository.SystemTenantRepository;
import com.snow.service.IAuthComponent;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 获取登录用户
 *
 * @author by 13119
 */
@Slf4j
@Getter
@Component
@AllArgsConstructor
public class AuthComponent implements IAuthComponent {
    final StringRedisTemplate stringRedisTemplate;

    final AuthConfig authConfig;

    final SystemTenantRepository systemTenantRepository;


    public String generateToken(ILoginUser loginUser) {
        String token = IdUtil.fastSimpleUUID();
        loginUser.setExpireTime(DateUtil.offset(new Date(), DateField.SECOND, authConfig.getAuthExpireSeconds()));
        stringRedisTemplate.opsForValue().set(
                token
                , JSONUtil.toJsonStr(loginUser)
                , authConfig.getAuthExpireSeconds()
                , TimeUnit.SECONDS);
        return token;
    }

    @Transactional(readOnly = true)
    @Override
    public SystemTenant getCurrentTenant() {
        return systemTenantRepository.findById(getCurrentUser().getTenantId()).orElseThrow(() -> new CommonRuntimeException("未找到租户信息"));
    }

    public Integer getTenantIdByHeader(){
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes != null ? servletRequestAttributes.getRequest() : null;
        if (request == null) {
            log.error("当前线程无法获取Request，导致无法获取当前请求房屋所属租户");
            throw new LoginUserNotFoundException();
        }
        String tenantId = request.getHeader("x-tenantId");
        return Integer.parseInt(StrUtil.blankToDefault(tenantId, "0"));
    }
    @Override
    public SystemTenant findTenant(String subdomain) {
        return systemTenantRepository.findBy("subdomain", subdomain);
    }

    public void cancelToken() {
        stringRedisTemplate.delete(getToken());
    }

    public JSONObject parseToken(String token) {
        boolean verify = false;
        try {
            verify = JWTUtil.verify(token, authConfig.getTokenKey().getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("登录令牌解析错误，请联系管理员", e);
            throw new CommonRuntimeException("登录令牌解析失败，请联系管理员");
        }
        if (!verify) {
            throw new LoginUserNotFoundException();
        }
        return JWTUtil.parseToken(token).getPayloads();
    }


    String getToken() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes != null ? servletRequestAttributes.getRequest() : null;
        if (request == null) {
            log.error("当前线程无法获取Request，导致无法获取登录用户");
            throw new LoginUserNotFoundException();
        }
        String token = request.getHeader(authConfig.getAuthHeaderFlag());
        if (StrUtil.isBlank(token)) {
            throw new LoginUserNotFoundException();
        }
        return token;
    }

    public ILoginUser getCurrentUser() {
        String cashUser = stringRedisTemplate.opsForValue().get(getToken());
        if (StrUtil.isBlank(cashUser)) {
            log.error("登录信息不存在或已过期");
            throw new LoginUserNotFoundException();
        }
        ILoginUser loginUser = null;
        try {
            loginUser = (ILoginUser) JSONUtil.toBean(cashUser, Class.forName(JSONUtil.parseObj(cashUser).getStr("className")));
        } catch (ClassNotFoundException e) {
            throw new CommonRuntimeException("当前登录用户转换失败，请联系管理员");
        }
        if (loginUser.getExpireTime() == null) {
            log.error("无法获取Token过期时间");
            throw new LoginUserNotFoundException();
        }
        if (loginUser.getExpireTime().before(new Date())) {
            log.error("Token已过期");
            throw new LoginUserNotFoundException();
        }
        return loginUser;
    }


    public Boolean havePermission(RequirePermission needPermission) {
        ILoginUser user = getCurrentUser();
        if (user.isSystemAdmin()) {
            return true;
        }
        return user.getPermissions().contains(needPermission.value());
    }

}
